package com.yuantu.judiciary.serviceimpl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import com.yuantu.judiciary.dao.*;
import com.yuantu.judiciary.exception.ServiceException;
import com.yuantu.judiciary.exception.enums.DisCountCodeExceptionEnum;
import com.yuantu.judiciary.model.domain.*;
import com.yuantu.judiciary.model.enums.AuthResultType;
import com.yuantu.judiciary.model.enums.PayState;
import com.yuantu.judiciary.model.form.DiscountCodeForm;
import com.yuantu.judiciary.model.form.DiscountCodeStatusForm;
import com.yuantu.judiciary.model.form.ErrorForm;
import com.yuantu.judiciary.model.vo.*;
import com.yuantu.judiciary.security.handler.DiscountCodeExcelImportVerifyHandler;
import com.yuantu.judiciary.service.IDiscountCodeService;
import com.yuantu.judiciary.utils.AssertUtil;
import com.yuantu.judiciary.utils.ExcelUtil;
import com.yuantu.judiciary.utils.StringUtil;
import lombok.RequiredArgsConstructor;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 邀请码
 *
 * @author syw
 * @since 2023-08-18
 **/
@Service
@RequiredArgsConstructor
public class DiscountCodeServiceImpl implements IDiscountCodeService {


    private final IDiscountCodeDao discountCodeDao;

    private final IVipOrderDao orderDao;

    private final DiscountCodeExcelImportVerifyHandler discountCodeExcelImportVerifyHandler;

    private final IQueryRecordDao queryRecordDao;

    private final IAccountDao accountDao;


    @Override
    public DiscountCodeCheckResultVO checkDiscountValid(DiscountCodeCheckVO discountCodeCheck) {

        DiscountCodeCheckResultVO checkResult = new DiscountCodeCheckResultVO();
        DiscountCodeDO discountInfo = discountCodeDao.findDiscountCodeByDiscountCodeAndDeletedFalse(discountCodeCheck.getDiscountCode());
        if (discountInfo == null || Boolean.FALSE.equals(discountInfo.getIsValid())) {
            checkResult.setIsValid(false);
            return checkResult;
        }

        BigDecimal price = new BigDecimal(discountCodeCheck.getPrice().toString());
        BigDecimal discountPrice = new BigDecimal(discountInfo.getDiscountPrice().toString());
        BigDecimal subtract = price.subtract(discountPrice).setScale(2, RoundingMode.HALF_UP);
        AssertUtil.assertTrue(subtract.doubleValue() > 0.0, DisCountCodeExceptionEnum.DISCOUNT_PRICE_ERROR);
        checkResult.setDiscountPrice(subtract.doubleValue());
        checkResult.setIsValid(true);
        return checkResult;
    }

    @Override
    public Long updateDiscountCodeInfo(DiscountCodeVO discountCode) {
        Long discountCodeId = discountCode.getDiscountCodeId();
        if (discountCodeId == null) {
            DiscountCodeDO discountCodeInfo = new DiscountCodeDO();
            BeanUtils.copyProperties(discountCode, discountCodeInfo);
            discountCodeInfo.setIsValid(true);
            discountCodeInfo.setDeleted(false);
            discountCodeInfo.setPointScale(1.0);
            discountCodeDao.save(discountCodeInfo);
            discountCodeId = discountCodeInfo.getId();
        } else {
            DiscountCodeDO existDiscountCode = discountCodeDao.findDiscountCodeByIdAndDeletedFalse(discountCodeId);
            if (existDiscountCode != null) {
                BeanUtils.copyProperties(discountCode, existDiscountCode);
                discountCodeDao.save(existDiscountCode);
            }
        }
        return discountCodeId;
    }

    @Transactional
    @Override
    public Boolean removeDiscountCodeInfo(List<Long> discountCodeIds) {
        discountCodeDao.removeDiscountCodeInfo(discountCodeIds);
        return true;
    }

    @Override
    public PageVO<DiscountCodeInfoVO> queryDiscountCodeInfos(DiscountCodeForm form, Pageable pageable) {

        Page<DiscountCodeDO> discountCodeInfos = discountCodeDao.findDiscountCodeInfosByForm(form, pageable);
        List<DiscountCodeDO> content = discountCodeInfos.getContent();
        List<Long> discountCodeIds = content.stream().map(DiscountCodeDO::getId).collect(Collectors.toList());
        Map<Long, List<VipOrderDO>> orderMap = new HashMap<>(8);
        if (!CollectionUtils.isEmpty(discountCodeIds)) {
            List<VipOrderDO> orderList = orderDao.findOrderInfosByDiscountCodeIdInAndPayStateAndDeletedFalse(discountCodeIds,
                    PayState.SUCCESS);
            if (form.getStartTime() != null || form.getEndTime() != null) {
                orderList = orderList.stream().filter(order -> (form.getStartTime() == null || order.getPayTime().after(form.getStartTime())) &&
                        (form.getEndTime() == null || order.getPayTime().before(form.getEndTime()))).collect(Collectors.toList());
            }
            List<VipOrderDO> aliOrders = orderList.stream().filter(s -> s.getPrepayId() == null).collect(Collectors.toList());
            List<VipOrderDO> wxOrders = orderList.stream().filter(s -> s.getPrepayId() != null).collect(Collectors.toList());
            List<String> tradeNoInfos = aliOrders.stream().map(VipOrderDO::getTradeNo).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(tradeNoInfos)) {
                List<QueryRecordDO> queryRecordInfos = queryRecordDao.findByAuthorizedResultAndTradeNoIn(AuthResultType.AUTHORIZED, tradeNoInfos);
                tradeNoInfos = queryRecordInfos.stream().map(QueryRecordDO::getTradeNo).collect(Collectors.toList());
            }
            List<String> finalTradeNoInfos = tradeNoInfos;
            aliOrders = aliOrders.stream().filter(s -> finalTradeNoInfos.contains(s.getTradeNo())).collect(Collectors.toList());
            wxOrders.addAll(aliOrders);
            orderMap = wxOrders.stream().collect(Collectors.groupingBy(VipOrderDO::getDiscountCodeId));
        }
        List<Long> accountIds = content.stream().filter(s -> StringUtil.isNotBlank(s.getAccountInfo()))
                .map(s -> Long.parseLong(s.getAccountInfo())).collect(Collectors.toList());
        Map<Long, IAccountDao.AccountUserInfo> accountMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(accountIds)) {
            accountMap = accountDao.getAccountInfo(accountIds).stream().
                    collect(Collectors.toMap(IAccountDao.AccountUserInfo::getAccountId, Function.identity(), (s1, s2) -> s2));
        }
        List<DiscountCodeInfoVO> discountCodeInfoList = new ArrayList<>();
        for (DiscountCodeDO discountCode : content) {
            DiscountCodeInfoVO discountCodeInfo = new DiscountCodeInfoVO();
            BeanUtils.copyProperties(discountCode, discountCodeInfo);
            discountCodeInfo.setDiscountCodeId(discountCode.getId());
            discountCodeInfo.setUseTimes(orderMap.getOrDefault(discountCode.getId(), new ArrayList<>()).size());
            if(!StringUtil.isEmpty(discountCode.getAccountInfo())){
                discountCodeInfo.setAccountInfo(accountMap.get(Long.parseLong(discountCode.getAccountInfo())) == null ? null
                        : accountMap.get(Long.parseLong(discountCode.getAccountInfo())).getUsername());
            }
            discountCodeInfoList.add(discountCodeInfo);
        }
        return new PageVO<>(discountCodeInfos.getTotalElements(), discountCodeInfoList);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean changeDiscountCodeStatus(DiscountCodeStatusForm form) {
        discountCodeDao.changeDiscountCodeStatus(form);
        return true;
    }

    @Override
    public void uploadDiscountCodeInfos(MultipartFile file, HttpServletResponse response) {

        ExcelImportResult<DiscountCodeImportVO> excelDate = getExcelDate(file, DiscountCodeImportVO.class);
        List<ErrorForm> failReport = excelDate.getFailList().stream()
                .map(entity -> {
                    int line = entity.getRowNum() == null ? 0 : entity.getRowNum() + 1;
                    return new ErrorForm(line, entity.getErrorMsg());
                }).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(excelDate.getList())) {
            for (DiscountCodeImportVO discountCodeImport : excelDate.getList()) {
                DiscountCodeVO discountCode = new DiscountCodeVO();
                BeanUtils.copyProperties(discountCodeImport, discountCode);
                updateDiscountCodeInfo(discountCode);
            }
        }
        if (!CollectionUtils.isEmpty(failReport)) {
            Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("错误信息", "Sheet1", ExcelType.XSSF),
                    ErrorForm.class, failReport);
            try {
                ExcelUtil.downLoadExcel("错误信息", response, workbook);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public void downloadExcelTemplate(HttpServletResponse response) {
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("优惠码信息", "Sheet1", ExcelType.XSSF),
                DiscountCodeImportVO.class, new ArrayList<>());
        try {
            ExcelUtil.downLoadExcel("优惠码信息", response, workbook);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<DiscountCodeUseDetailVO> discountUseDetail(Long discountCodeId) {
        DiscountCodeDO discountCode = discountCodeDao.findDiscountCodeByIdAndDeletedFalse(discountCodeId);
        AssertUtil.assertNotNull(discountCode, DisCountCodeExceptionEnum.DISCOUNT_CODE_NOT_EXIST);
        List<VipOrderDO> orderInfos = orderDao.findOrderInfosByDiscountCodeIdInAndPayStateAndDeletedFalse(Collections.singletonList(discountCodeId), PayState.SUCCESS);
        List<VipOrderDO> aliOrders = orderInfos.stream().filter(s -> s.getPrepayId() == null).collect(Collectors.toList());
        List<VipOrderDO> wxOrders = orderInfos.stream().filter(s -> s.getPrepayId() != null).collect(Collectors.toList());
        List<String> tradeNoInfos = aliOrders.stream().map(VipOrderDO::getTradeNo).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(tradeNoInfos)) {
            List<QueryRecordDO> queryRecordInfos = queryRecordDao.findByAuthorizedResultAndTradeNoIn(AuthResultType.AUTHORIZED, tradeNoInfos);
            tradeNoInfos = queryRecordInfos.stream().map(QueryRecordDO::getTradeNo).collect(Collectors.toList());
        }
        List<String> finalTradeNoInfos = tradeNoInfos;
        aliOrders = aliOrders.stream().filter(s -> finalTradeNoInfos.contains(s.getTradeNo())).collect(Collectors.toList());
        wxOrders.addAll(aliOrders);
        List<Long> creatorIds = wxOrders.stream().map(VipOrderDO::getCreatorId).collect(Collectors.toList());
        Map<Long, IAccountDao.AccountUserInfo> accountMap = new HashMap<>(8);
        if (!CollectionUtils.isEmpty(creatorIds)) {
            List<IAccountDao.AccountUserInfo> accountInfos = accountDao.getAccountInfo(creatorIds);
            accountMap = accountInfos.stream().collect(Collectors.toMap(IAccountDao
                    .AccountUserInfo::getAccountId, Function.identity()));
        }
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
        List<DiscountCodeUseDetailVO> result = new ArrayList<>();
        int orderNum = 1;
        orderInfos = orderInfos.stream().sorted(Comparator.comparing(VipOrderDO::getCreateTime)).collect(Collectors.toList());
        for (VipOrderDO orderInfo : orderInfos) {
            DiscountCodeUseDetailVO discountCodeUseDetail = new DiscountCodeUseDetailVO();
            discountCodeUseDetail.setOrderNum(orderNum++);
            Long creatorId = orderInfo.getCreatorId();
            IAccountDao.AccountUserInfo accountUserInfo = accountMap.get(creatorId);
            if (accountUserInfo != null) {
                discountCodeUseDetail.setUserName(accountUserInfo.getUsername());
                discountCodeUseDetail.setAccountInfo(accountUserInfo.getPhone());
            }
            discountCodeUseDetail.setDiscountCode(discountCode.getDiscountCode());
            discountCodeUseDetail.setUseDate(dateFormat.format(orderInfo.getCreateTime()));
            result.add(discountCodeUseDetail);
        }
        return result;
    }

    @Override
    public void discountUseDetailExport(Long discountCodeId, HttpServletResponse response) {
        List<DiscountCodeUseDetailVO> discountCodeUseDetails = discountUseDetail(discountCodeId);
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("推荐码使用明细", "Sheet1", ExcelType.XSSF),
                DiscountCodeUseDetailVO.class, discountCodeUseDetails);
        try {
            ExcelUtil.downLoadExcel("推荐码使用明细.xlsx", response, workbook);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void downloadDiscountListInfos(HttpServletResponse response) {

        List<DiscountCodeDO> discountCodeInfos = discountCodeDao.findAllInfos();
        List<Long> discountCodeIds = discountCodeInfos.stream().map(DiscountCodeDO::getId).collect(Collectors.toList());
        Map<Long, List<VipOrderDO>> orderMap = new HashMap<>(8);
        if (!CollectionUtils.isEmpty(discountCodeIds)) {
            List<VipOrderDO> orderList = orderDao.findOrderInfosByDiscountCodeIdInAndPayStateAndDeletedFalse(discountCodeIds,
                    PayState.SUCCESS);
            List<VipOrderDO> aliOrders = orderList.stream().filter(s -> s.getPrepayId() == null).collect(Collectors.toList());
            List<VipOrderDO> wxOrders = orderList.stream().filter(s -> s.getPrepayId() != null).collect(Collectors.toList());
            List<String> tradeNoInfos = aliOrders.stream().map(VipOrderDO::getTradeNo).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(tradeNoInfos)) {
                List<QueryRecordDO> queryRecordInfos = queryRecordDao.findByAuthorizedResultAndTradeNoIn(AuthResultType.AUTHORIZED, tradeNoInfos);
                tradeNoInfos = queryRecordInfos.stream().map(QueryRecordDO::getTradeNo).collect(Collectors.toList());
            }
            List<String> finalTradeNoInfos = tradeNoInfos;
            aliOrders = aliOrders.stream().filter(s -> finalTradeNoInfos.contains(s.getTradeNo())).collect(Collectors.toList());
            wxOrders.addAll(aliOrders);
            orderMap = wxOrders.stream().collect(Collectors.groupingBy(VipOrderDO::getDiscountCodeId));
        }
        List<Long> accountIds = discountCodeInfos.stream().map(s -> Long.parseLong(s.getAccountInfo())).collect(Collectors.toList());
        Map<Long, IAccountDao.AccountUserInfo> accountMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(accountIds)) {
            accountMap = accountDao.getAccountInfo(accountIds).stream().
                    collect(Collectors.toMap(IAccountDao.AccountUserInfo::getAccountId, Function.identity(), (s1, s2) -> s2));
        }
        List<DiscountCodeInfoVO> discountCodeInfoList = new ArrayList<>();
        for (DiscountCodeDO discountCode : discountCodeInfos) {
            DiscountCodeInfoVO discountCodeInfo = new DiscountCodeInfoVO();
            BeanUtils.copyProperties(discountCode, discountCodeInfo);
            discountCodeInfo.setDiscountCodeId(discountCode.getId());
            discountCodeInfo.setUseTimes(orderMap.getOrDefault(discountCode.getId(), new ArrayList<>()).size());
            discountCodeInfo.setAccountInfo(accountMap.get(Long.parseLong(discountCode.getAccountInfo())) == null ? null
                    : accountMap.get(Long.parseLong(discountCode.getAccountInfo())).getUsername());
            discountCodeInfoList.add(discountCodeInfo);
        }
        discountCodeInfoList = discountCodeInfoList.stream().sorted(Comparator.
                comparing(DiscountCodeInfoVO::getUseTimes, Comparator.reverseOrder())).collect(Collectors.toList());
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("推荐码有关信息", "Sheet1", ExcelType.XSSF),
                DiscountCodeInfoVO.class, discountCodeInfoList);
        try {
            ExcelUtil.downLoadExcel("推荐码有关信息.xlsx", response, workbook);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    private ExcelImportResult<DiscountCodeImportVO> getExcelDate(MultipartFile file, Class<DiscountCodeImportVO> discountCodeImportClass) {
        try {
            ImportParams params = new ImportParams();
            params.setTitleRows(1);
            params.setHeadRows(1);
            params.setStartRows(0);
            params.setNeedVerify(true);
            params.setVerifyHandler(discountCodeExcelImportVerifyHandler);
            return ExcelImportUtil.importExcelMore(file.getInputStream(), discountCodeImportClass, params);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
